<template>
  <div class="dump-management">
    <div class="page-header">
      <div class="header-content">
                 <div class="title-section">
           <h1>日志转储管理</h1>
           <p class="subtitle">通过本页面可灵活控制转储日志的解压数量及生效范围，优化系统性能的同时确保关键日志可用。</p>
         </div>
        <div class="header-actions">
          <!-- 新建转储按钮已删除 -->
        </div>
        <div class="status-indicators">
          <div class="status-item">
            <div class="status-icon framework">
              <n-icon size="20"><DocumentOutline /></n-icon>
            </div>
            <div class="status-info">
              <span class="status-count">{{ frameworkDumpList.length }}</span>
              <span class="status-label">框架日志</span>
            </div>
          </div>
          <div class="status-item">
            <div class="status-icon operation">
              <n-icon size="20"><DocumentOutline /></n-icon>
            </div>
            <div class="status-info">
              <span class="status-count">{{ operationDumpList.length }}</span>
              <span class="status-label">操作日志</span>
            </div>
          </div>
          <div class="status-item">
            <div class="status-icon debug">
              <n-icon size="20"><DocumentOutline /></n-icon>
            </div>
            <div class="status-info">
              <span class="status-count">{{ debugDumpList.length }}</span>
              <span class="status-label">调试日志</span>
            </div>
          </div>
        </div>
      </div>
    </div>

         <div class="content-section">
               <!-- 框架日志转储文件 -->
        <n-card title="框架日志转储文件" class="dump-list-card">
          <n-table :bordered="false" :single-line="false">
            <thead>
              <tr>
                <th>转储文件</th>
                <th>文件大小</th>
                <th>创建时间</th>
                <th>
                  <span class="analysis-col-header">
                    <span>纳入分析</span>
                    <n-tooltip trigger="hover">
                      <template #trigger>
                        <span class="info-icon">
                          <n-icon size="18" color="#2080f0"><InformationCircleOutline /></n-icon>
                        </span>
                      </template>
                      <div style="white-space: pre-line; max-width: 360px;">
                        纳入分析允许您选择性加载并标记需要参与日志分析的转储文件。系统仅对标记为“纳入分析”的日志进行解压和处理，未纳入的文件将保持压缩状态，避免不必要的性能开销。
                      </div>
                    </n-tooltip>
                  </span>
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="dump in frameworkDumpList" :key="dump.id">
                <td>{{ dump.name }}</td>
                <td>{{ formatFileSize(dump.fileSize) }}</td>
                <td>{{ formatDate(dump.createTime) }}</td>
                <td>
                  <n-switch :value="isIncluded('framework', dump.id)" size="small" @update:value="(v)=>toggleIncluded('framework', dump.id, v)" />
                </td>
              </tr>
            </tbody>
          </n-table>
         
         <div v-if="frameworkDumpList.length === 0" class="empty-state">
           <n-empty description="暂无框架日志转储文件">
             <template #icon>
               <n-icon size="64" color="#d9d9d9">
                 <DocumentOutline />
               </n-icon>
             </template>
           </n-empty>
         </div>
       </n-card>

               <!-- 操作日志转储文件 -->
        <n-card title="操作日志转储文件" class="dump-list-card">
          <n-table :bordered="false" :single-line="false">
            <thead>
              <tr>
                <th>转储文件</th>
                <th>文件大小</th>
                <th>创建时间</th>
                <th>
                  <span class="analysis-col-header">
                    <span>纳入分析</span>
                    <n-tooltip trigger="hover">
                      <template #trigger>
                        <span class="info-icon">
                          <n-icon size="18" color="#2080f0"><InformationCircleOutline /></n-icon>
                        </span>
                      </template>
                      <div style="white-space: pre-line; max-width: 360px;">
                        纳入分析允许您选择性加载并标记需要参与日志分析的转储文件。系统仅对标记为“纳入分析”的日志进行解压和处理，未纳入的文件将保持压缩状态，避免不必要的性能开销。
                      </div>
                    </n-tooltip>
                  </span>
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="dump in operationDumpList" :key="dump.id">
                <td>{{ dump.name }}</td>
                <td>{{ formatFileSize(dump.fileSize) }}</td>
                <td>{{ formatDate(dump.createTime) }}</td>
                <td>
                  <n-switch :value="isIncluded('operation', dump.id)" size="small" @update:value="(v)=>toggleIncluded('operation', dump.id, v)" />
                </td>
              </tr>
            </tbody>
          </n-table>
         
         <div v-if="operationDumpList.length === 0" class="empty-state">
           <n-empty description="暂无操作日志转储文件">
             <template #icon>
               <n-icon size="64" color="#d9d9d9">
                 <DocumentOutline />
               </n-icon>
             </template>
           </n-empty>
         </div>
       </n-card>

               <!-- 调试日志转储文件 -->
        <n-card title="调试日志转储文件" class="dump-list-card">
          <n-table :bordered="false" :single-line="false">
            <thead>
              <tr>
                <th>转储文件</th>
                <th>文件大小</th>
                <th>创建时间</th>
                <th>
                  <span class="analysis-col-header">
                    <span>纳入分析</span>
                    <n-tooltip trigger="hover">
                      <template #trigger>
                        <span class="info-icon">
                          <n-icon size="18" color="#2080f0"><InformationCircleOutline /></n-icon>
                        </span>
                      </template>
                      <div style="white-space: pre-line; max-width: 360px;">
                        纳入分析允许您选择性加载并标记需要参与日志分析的转储文件。系统仅对标记为“纳入分析”的日志进行解压和处理，未纳入的文件将保持压缩状态，避免不必要的性能开销。
                      </div>
                    </n-tooltip>
                  </span>
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="dump in debugDumpList" :key="dump.id">
                <td>{{ dump.name }}</td>
                <td>{{ formatFileSize(dump.fileSize) }}</td>
                <td>{{ formatDate(dump.createTime) }}</td>
                <td>
                  <n-switch :value="isIncluded('debug', dump.id)" size="small" @update:value="(v)=>toggleIncluded('debug', dump.id, v)" />
                </td>
              </tr>
            </tbody>
          </n-table>
         
         <div v-if="debugDumpList.length === 0" class="empty-state">
           <n-empty description="暂无调试日志转储文件">
             <template #icon>
               <n-icon size="64" color="#d9d9d9">
                 <DocumentOutline />
               </n-icon>
             </template>
           </n-empty>
         </div>
       </n-card>
     </div>

         <!-- 创建转储对话框已删除 -->
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useWorkspaceStore } from '../stores/workspace'
import { invoke } from '@tauri-apps/api/core'
import { LOG_PATHS } from '../config/logPaths'
import { DocumentOutline, InformationCircleOutline } from '@vicons/ionicons5'
import { NCard, NTable, NEmpty, NIcon, NSwitch, NTooltip } from 'naive-ui'
import { useDumpStore } from '../stores/dumps'
import { listWorkspacePaths, findWorkspacePathsByRegex, getArchiveEntry, saveDumpLogData, deleteDumpLogData } from '../utils/archiveDb'
import pako from 'pako'

const router = useRouter()
const workspaceStore = useWorkspaceStore()
const dumpStore = useDumpStore()

// 响应式数据
// 新建转储相关变量已删除

function isTauriEnv(): boolean {
  if (typeof window === 'undefined') return false
  const w = window as any
  // Tauri v2 挂载了 __TAURI__ 到 window
  return !!w.__TAURI__
}

// 按维度分类的转储数据（运行时从文件系统扫描填充）
const frameworkDumpList = ref<any[]>([])
const operationDumpList = ref<any[]>([])
const debugDumpList = ref<any[]>([])
const tauriAvailable = ref<boolean>(isTauriEnv())

// 处理转储文件纳入分析开关变化
async function handleDumpIncludeChange(dumpType: 'framework' | 'operation' | 'app', dumpFile: any, included: boolean) {
  console.log(`[Dump] ${dumpType} 转储文件 ${dumpFile.name} 纳入分析状态变更:`, included)
  
  const workspaceId = workspaceStore.currentWorkspaceId
  if (!workspaceId) {
    console.error('[Dump] 没有当前工作区ID')
    return
  }
  
  if (included) {
    // 开启时：解压并保存转储文件数据
    await processDumpFile(dumpType, dumpFile)
    
    // 清除相关缓存，这样下次加载日志时会重新包含转储数据
    if (typeof window !== 'undefined' && (window as any).__clearLogCache) {
      (window as any).__clearLogCache(dumpType)
      console.log(`[Dump] 已清除 ${dumpType} 日志缓存`)
    }
  } else {
    // 关闭时：删除对应的转储数据并清除缓存
    try {
      console.log(`[Dump] 开始删除 ${dumpType} 转储文件 ${dumpFile.name} 的数据`)
      
      // 从转储文件名提取索引号 (例如: app.log.1.gz -> 1)
      const indexMatch = dumpFile.name.match(/\.(\d+)\.gz$/)
      if (indexMatch) {
        const dumpIndex = parseInt(indexMatch[1])
        await deleteDumpLogData(workspaceId, dumpType, dumpIndex)
        console.log(`[Dump] 成功删除 ${dumpType} 转储文件索引 ${dumpIndex} 的数据`)
      } else {
        console.warn(`[Dump] 无法从文件名 ${dumpFile.name} 提取索引号`)
      }
    } catch (error) {
      console.error(`[Dump] 删除 ${dumpType} 转储数据失败:`, error)
    }
    
    // 清除相关缓存，移除转储数据的影响
    if (typeof window !== 'undefined' && (window as any).__clearLogCache) {
      (window as any).__clearLogCache(dumpType)
      console.log(`[Dump] 已清除 ${dumpType} 日志缓存`)
    }
    
    console.log(`[Dump] ${dumpType} 转储文件 ${dumpFile.name} 已从分析中移除`)
  }
}

// 处理转储文件：解压并保存
async function processDumpFile(dumpType: 'framework' | 'operation' | 'app', dumpFile: any) {
  const wsId = workspaceStore.currentWorkspace?.id
  if (!wsId) {
    console.error('[Dump] 没有当前工作区')
    return
  }

  try {
    console.log(`[Dump] 开始处理 ${dumpType} 转储文件:`, dumpFile.name)
    
    // 从 IndexedDB 获取压缩文件数据
    const entry = await getArchiveEntry(wsId, dumpFile.id)
    if (!entry || !entry.content || !(entry.content instanceof Blob)) {
      console.error('[Dump] 无法获取转储文件数据:', dumpFile.id)
      return
    }

    console.log(`[Dump] 获取到压缩文件，大小: ${entry.content.size} 字节`)
    
    // 读取压缩文件内容
    const compressedData = new Uint8Array(await entry.content.arrayBuffer())
    
    // 使用 pako 解压 gzip 数据
    console.log(`[Dump] 开始解压 ${dumpType} 转储文件...`)
    const decompressedData = pako.inflate(compressedData, { to: 'string' })
    
    console.log(`[Dump] 解压完成，解压后大小: ${(decompressedData.length / 1024 / 1024).toFixed(2)} MB`)
    
    // 保存解压后的日志数据
    await saveDumpLogData(wsId, dumpType, dumpFile.index, decompressedData)
    
    console.log(`[Dump] ${dumpType} 转储文件 ${dumpFile.name} 处理完成`)
    
  } catch (error) {
    console.error(`[Dump] 处理 ${dumpType} 转储文件失败:`, error)
  }
}

// 方法
const formatDate = (date: Date) => {
  return date instanceof Date ? date.toLocaleString('zh-CN') : new Date(date).toLocaleString('zh-CN')
}

const formatFileSize = (bytes: number) => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// createDump方法已删除

const viewDump = (dump: any) => {
  console.log('查看转储:', dump)
  // 这里可以跳转到转储详情页面
}

const extractDump = (dump: any) => {
  console.log('解压转储:', dump)
}

const activateDump = (dump: any) => {
  console.log('生效转储:', dump)
}

const downloadDump = (dump: any) => {
  console.log('下载转储:', dump)
  // 这里实现下载逻辑
}

const deleteDump = (dump: any) => {
  console.log('删除转储:', dump)
  // 这里实现删除逻辑，从对应的列表中删除
  let index = frameworkDumpList.value.findIndex(item => item.id === dump.id)
  if (index > -1) {
    frameworkDumpList.value.splice(index, 1)
    return
  }
  
  index = operationDumpList.value.findIndex(item => item.id === dump.id)
  if (index > -1) {
    operationDumpList.value.splice(index, 1)
    return
  }
  
  index = debugDumpList.value.findIndex(item => item.id === dump.id)
  if (index > -1) {
    debugDumpList.value.splice(index, 1)
    return
  }
}

// 扫描指定目录下的转储文件，命名规则：{baseName}{index}.gz，index从1递增
async function scanDumpFiles(baseDirAbs: string, baseName: string) {
  try {
    if (!(await isTauriEnv())) return []
    // 动态加载，避免非 Tauri 环境报错
    const fsMod = await import('@tauri-apps/plugin-fs')
    const pathMod = await import('@tauri-apps/api/path')
    const { readDir, stat } = fsMod
    const { join } = pathMod
    const entries = await readDir(baseDirAbs)
    console.log('[Dump][scanDumpFiles] baseDirAbs=', baseDirAbs, 'baseName=', baseName)
    console.log('[Dump][scanDumpFiles] entries=', entries.map(e => e.name))
    const pattern = new RegExp(`^${baseName}\\d+\\.gz$`)
    const matched = entries.filter(e => (!('children' in e) || !e.children) && e.name && pattern.test(e.name as string))
    console.log('[Dump][scanDumpFiles] pattern=', pattern, 'matched=', matched.map(m => m.name))
    const items: any[] = []
    for (const entry of matched) {
      const filePath = await join(baseDirAbs, entry.name!)
      const s = await stat(filePath)
      items.push({
        id: filePath,
        name: entry.name,
        fileSize: (s as any).size ?? 0,
        createTime: ((s as any).birthtime ?? (s as any).mtime) ? new Date(((s as any).birthtime ?? (s as any).mtime) as any) : new Date(),
        included: false
      })
    }
    // index 排序（按数字顺序）
    items.sort((a, b) => {
      const ai = parseInt(a.name.replace(/[^0-9]/g, '')) || 0
      const bi = parseInt(b.name.replace(/[^0-9]/g, '')) || 0
      return ai - bi
    })
    return items
  } catch (err) {
    console.error('扫描转储文件失败:', err)
    return []
  }
}

function dirnameLike(p: string): string {
  const parts = p.split('/')
  parts.pop()
  return parts.join('/')
}

function basenameLike(p: string): string {
  const parts = p.split('/')
  return parts[parts.length - 1] || ''
}

function escapeRegExp(str: string): string {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

onMounted(async () => {
  console.log('[Dump] 转储管理页面已加载')
  // 初始化持久化
  dumpStore.loadFromStorage()
  tauriAvailable.value = isTauriEnv()
  let wsRoot = workspaceStore.currentWorkspace?.filePath
  console.log('[Dump] currentWorkspace.filePath=', wsRoot)
  if (!wsRoot) {
    console.warn('当前工作区未设置文件根目录(filePath)，跳过扫描')
    return
  }
  // 如果是 tar/tar.gz 归档，先请求后端自动解压并返回解压根目录（仅 Tauri 有效）
  if (tauriAvailable.value && /\.(tar|tar\.gz|tgz|gz)$/i.test(wsRoot)) {
    try {
      const extractedRoot = await invoke<string>('extract_workspace_archive', { archivePath: wsRoot })
      console.log('[Dump] archive extracted to:', extractedRoot)
      wsRoot = extractedRoot
    } catch (e) {
      console.error('[Dump] 归档解压失败:', e)
      return
    }
  }
  // 三类日志所在根目录（与对应日志同目录）
  const dumpRootRel = dirnameLike(LOG_PATHS.LOG_DUMP.FRAMEWORK) // dump_info/LogDump
  let dumpRootAbs: string | undefined
  if (tauriAvailable.value) {
    const { join } = await import('@tauri-apps/api/path')
    dumpRootAbs = await join(wsRoot, dumpRootRel)
    console.log('[Dump] dumpRootRel=', dumpRootRel, 'candidate dumpRootAbs=', dumpRootAbs)
  }

  // 验证候选目录是否可读；若失败，尝试把 wsRoot 作为 LogDump 目录直接使用
  if (tauriAvailable.value) {
    try {
      const fsMod = await import('@tauri-apps/plugin-fs')
      await fsMod.readDir(dumpRootAbs!)
    } catch (e) {
      console.warn('[Dump] 读取候选目录失败，将尝试使用wsRoot作为LogDump目录:', e)
      dumpRootAbs = wsRoot
    }
  }

  // 从配置推导基础文件名（如 framework.log / operation.log / app.log）
  const frameworkBase = basenameLike(LOG_PATHS.LOG_DUMP.FRAMEWORK) // framework.log
  const operationBase = basenameLike(LOG_PATHS.LOG_DUMP.OPERATION) // operation.log
  const appBase = basenameLike(LOG_PATHS.LOG_DUMP.APP) // app.log

  // 扫描规则：{base}.log.{index}.gz → 这里 base 已含 .log，因此匹配 `${base}.(\d+).gz`
  async function scanWithBase(baseFile: string) {
    const escaped = escapeRegExp(baseFile)
    const pattern = new RegExp(`^${escaped}\\.(\\d+)\\.gz$`)
    const items: any[] = []
    const seen = new Set<string>()

    console.log('[Dump] scanWithBase called with baseFile:', baseFile)
    console.log('[Dump] escaped pattern:', escaped)
    console.log('[Dump] final regex:', pattern)

    // 1) Tauri: 直接扫描文件系统
    if (tauriAvailable.value && dumpRootAbs) {
      try {
        const fsMod = await import('@tauri-apps/plugin-fs')
        const pathMod = await import('@tauri-apps/api/path')
        const { readDir, stat } = fsMod
        const { join } = pathMod
        console.log('[Dump] scanning directory (tauri):', dumpRootAbs, 'for base:', baseFile)
        const entries = await readDir(dumpRootAbs)
        const matched = entries.filter(e => (!('children' in e) || !e.children) && e.name && pattern.test(e.name as string))
        console.log('[Dump] tauri entries found:', entries.length, 'matched:', matched.length)
        for (const entry of matched) {
          const filePath = await join(dumpRootAbs, entry.name!)
          const s = await stat(filePath)
          const m = (entry.name as string).match(pattern)
          const idx = m ? parseInt(m[1]) : 0
          const rec = {
            id: filePath,
            name: entry.name,
            fileSize: (s as any).size ?? 0,
            createTime: ((s as any).birthtime ?? (s as any).mtime) ? new Date(((s as any).birthtime ?? (s as any).mtime) as any) : new Date(),
            included: false,
            index: idx
          }
          if (!seen.has(rec.id)) { items.push(rec); seen.add(rec.id) }
        }
      } catch (e) {
        console.warn('[Dump] tauri scan failed, continue with IndexedDB fallback:', e)
      }
    }

    // 2) IndexedDB: 合并前端已解压路径
    try {
      const wsId = workspaceStore.currentWorkspace?.id || ''
      console.log('[Dump] IndexedDB scan for workspace:', wsId, 'pattern:', pattern)
      // 直接用正则匹配，只取最多400个压缩包
      const matched = await findWorkspacePathsByRegex(wsId, pattern, 400)
      console.log('[Dump] IndexedDB matched paths:', matched.length)
      matched.forEach(p => console.log('[Dump] IndexedDB path:', p, 'basename:', basenameLike(p)))
      
      for (const p of matched) {
        const name = basenameLike(p)
        const m = name.match(pattern)
        const idx = m ? parseInt(m[1]) : 0
        console.log('[Dump] processing path:', p, 'name:', name, 'match:', m, 'idx:', idx)
        
        // 从 IndexedDB 获取真实的文件信息
        let fileSize = 0
        let createTime = new Date()
        try {
          const entry = await getArchiveEntry(wsId, p)
          if (entry) {
            fileSize = entry.size || 0
            // 如果有内容，尝试从 Blob 获取更准确的大小
            if (entry.content && entry.content instanceof Blob) {
              fileSize = entry.content.size
            }
            console.log('[Dump] got entry info:', { path: p, size: fileSize })
          }
        } catch (e) {
          console.warn('[Dump] failed to get entry info for:', p, e)
        }
        
        // 根据索引号推断相对创建时间（索引越大表示越新）
        // 假设每个转储文件间隔1小时
        const baseTime = new Date('2024-01-01T00:00:00')
        createTime = new Date(baseTime.getTime() + idx * 60 * 60 * 1000)
        
        const rec = { id: p, name, fileSize, createTime, included: false, index: idx }
        if (!seen.has(rec.id)) { items.push(rec); seen.add(rec.id) }
      }
    } catch (e) {
      console.warn('[Dump] IndexedDB scan failed:', e)
    }

    console.log('[Dump] final items for base', baseFile, ':', items.length)
    items.sort((a, b) => a.index - b.index)
    return items
  }

  console.log('[Dump] bases=', { frameworkBase, operationBase, appBase })
  frameworkDumpList.value = await scanWithBase(frameworkBase)
  operationDumpList.value = await scanWithBase(operationBase)
  debugDumpList.value = await scanWithBase(appBase)
  console.log('[Dump] result counts=', {
    framework: frameworkDumpList.value.length,
    operation: operationDumpList.value.length,
    app: debugDumpList.value.length
  })
})

function isIncluded(type: 'framework'|'operation'|'debug', id: string) {
  return dumpStore.isIncluded(workspaceStore.currentWorkspace?.id, type, id)
}

async function toggleIncluded(type: 'framework'|'operation'|'debug', id: string, v: boolean) {
  // 更新存储状态
  dumpStore.setIncluded(workspaceStore.currentWorkspace?.id, type, id, v)
  
  // 找到对应的转储文件对象
  let dumpFile: any = null
  let dumpType: 'framework' | 'operation' | 'app' = 'framework'
  
  if (type === 'framework') {
    dumpFile = frameworkDumpList.value.find(d => d.id === id)
    dumpType = 'framework'
  } else if (type === 'operation') {
    dumpFile = operationDumpList.value.find(d => d.id === id)
    dumpType = 'operation'
  } else if (type === 'debug') {
    dumpFile = debugDumpList.value.find(d => d.id === id)
    dumpType = 'app' // debug 对应 app 类型
  }
  
  if (dumpFile) {
    // 调用处理函数
    await handleDumpIncludeChange(dumpType, dumpFile, v)
  } else {
    console.warn('[Dump] 未找到转储文件:', type, id)
  }
}
</script>

<style scoped>
.dump-management {
  padding: 0;
  background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
  min-height: 100vh;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.page-header {
  margin-bottom: 20px;
  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
  border-radius: 0;
  padding: 20px 24px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
}

.header-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 32px;
}

.title-section {
  flex: 1;
  min-width: 0;
  max-width: 65%;
}

.title-section h1 {
  margin: 0 0 12px 0;
  font-size: 32px;
  font-weight: 700;
  background: linear-gradient(135deg, #1e40af 0%, #3b82f6 50%, #60a5fa 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  letter-spacing: -0.5px;
}

.subtitle {
  margin: 0;
  color: #64748b;
  font-size: 15px;
  line-height: 1.6;
  max-width: 100%;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.content-section {
  margin-bottom: 20px;
}

.dump-list-card {
  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
  border-radius: 0;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.8);
  margin-bottom: 16px;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;
  position: relative;
}

.dump-list-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}

.dump-list-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 50%, #3b82f6 100%);
  border-radius: 0;
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

.dump-list-card::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
  transition: left 0.8s ease;
  pointer-events: none;
}

.dump-list-card:hover::after {
  left: 100%;
}

.empty-state {
  padding: 40px 0;
  text-align: center;
  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
  margin: 16px -20px -20px -20px;
  border-radius: 0 0 16px 16px;
}

.feature-card {
  height: 100%;
  transition: all 0.3s ease;
}

.feature-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.analysis-col-header {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
  color: #374151;
}

.analysis-col-header .info-icon {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  transition: all 0.2s ease;
}

.analysis-col-header .info-icon:hover {
  transform: scale(1.1);
  color: #1d4ed8;
}

/* 表格样式优化 */
:deep(.n-table) {
  border-radius: 12px;
  overflow: hidden;
}

:deep(.n-table th) {
  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
  color: #374151;
  font-weight: 600;
  font-size: 14px;
  padding: 12px 16px;
  border-bottom: 2px solid #e2e8f0;
}

:deep(.n-table td) {
  padding: 10px 16px;
  border-bottom: 1px solid #f1f5f9;
  color: #4b5563;
  font-size: 14px;
}

:deep(.n-table tbody tr:hover) {
  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
}

/* 开关样式优化 */
:deep(.n-switch) {
  transition: all 0.2s ease;
}

:deep(.n-switch--checked) {
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}

/* 卡片标题样式 */
:deep(.n-card-header) {
  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
  border-bottom: 1px solid #e2e8f0;
  padding: 20px 24px;
  margin: -20px -20px 16px -20px;
}

:deep(.n-card-header__main) {
  font-size: 18px;
  font-weight: 600;
  color: #1e293b;
  padding-left: 16px;
  padding-top: 4px;
}

/* 空状态图标样式 */
:deep(.n-empty__icon) {
  color: #94a3b8;
  opacity: 0.8;
}

:deep(.n-empty__description) {
  color: #64748b;
  font-size: 15px;
  margin-top: 16px;
}

/* 状态指示器样式 */
.status-indicators {
  display: flex;
  gap: 12px;
  margin-top: 12px;
  flex-shrink: 0;
  justify-content: flex-end;
  min-width: 400px;
}

.status-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  transition: all 0.3s ease;
  min-width: 110px;
}

.status-item:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

.status-icon {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: 600;
}

.status-icon.framework {
  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
}

.status-icon.operation {
  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}

.status-icon.debug {
  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}

.status-info {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.status-count {
  font-size: 20px;
  font-weight: 700;
  color: #1e293b;
  line-height: 1;
}

.status-label {
  font-size: 12px;
  color: #64748b;
  font-weight: 500;
  margin-top: 2px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .dump-management {
    padding: 0;
  }
  
  .page-header {
    padding: 20px 24px;
  }
  
  .title-section h1 {
    font-size: 28px;
  }
  
  .subtitle {
    font-size: 14px;
  }
  
  :deep(.n-table th),
  :deep(.n-table td) {
    padding: 12px 16px;
  }

  .status-indicators {
    flex-direction: column;
    gap: 16px;
  }
  
  .status-item {
    min-width: auto;
    padding: 14px 18px;
  }
  
  .status-count {
    font-size: 20px;
  }

}
</style> 